/*
 * Middleware
 * Copyright (C) 2010/11
 * Riccardo Pulvirenti, Giuseppe Ravidà, Andrea Tino
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANDABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses
 */

/*!
 * File: manager.cpp
 */

#include <iostream>
#include <string>
#include <exception>
#include <boost/thread.hpp>
#include <boost/asio.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <cstdlib>

#include "manager.hpp"
#include "utils.hpp"

using namespace middleware;

//-----------------------------------------------------------------------------
// Constructors and destructor
/* Default constructor */
Manager::Manager() {
	this->_config.man_ip_toui_bind = "127.0.0.1"; /* Not used to bind */
	this->_config.man_port_toui_bind = this->createPort();
	this->_config.man_ip_todisc = "127.0.0.1";
	this->_config.man_port_todisc = "0";
}
/* Configuration constructor */
Manager::Manager(ManagerConfig conf) {
	this->_config.man_ip_toui_bind = conf.man_ip_toui_bind; /* Not used to bind */
	this->_config.man_port_toui_bind = conf.man_port_toui_bind;
	this->_config.man_ip_todisc = conf.man_ip_todisc;
	this->_config.man_port_todisc = conf.man_port_todisc;
}
/* Destructor */
Manager::~Manager() {
	// Every member is statically allocated.
}
//-----------------------------------------------------------------------------
// Private members
/* Gets a random port */
std::string Manager::createPort() {
	return utils::GetRandomIntegralNumberStr(kManagerMinManPort,
			kManagerMaxManPort);
}
/* Thread execution flow | Sharing a SynQueue */
void Manager::exec() {
	// Execution flow for Manager
	std::cout << kManagerEtq << "Running!" << std::endl;
	this->bonjour(); /* Initialization sequence */
	try {
		/* Release thread to listen for incoming requests and enqueueing
		 * workflows in the queue. */
		std::cout << kManagerEtq << "Releasing Enqueuer..." << std::endl;
		this->_enqueuer_thread = boost::shared_ptr<boost::thread>(
				new boost::thread(boost::bind(&Manager::exec_enqueuer, this)));
		// Cycling and dequeueing from queue
		for (;;) {
			std::cout << kManagerEtq << "Dequeueing loop, " <<
					"Dequeueing workflow..." << std::endl;
			Workflow wf = this->_wf_synqueue.dequeue(); /* Blocking call */
			std::cout << kManagerEtq << "Dequeued workflow id=" <<
					wf.get_id() << ". WFQueue has now " <<
					this->_wf_synqueue.size() << " workflows." <<
					" Now managing dequeued wf..." << std::endl;
			// A thread will manage the workflow (multithread)
			boost::shared_ptr<boost::thread>(new boost::thread(
					boost::bind(&Manager::ManageWorkflow, this, wf)));
			//this->ManageWorkflow(wf); /* Managing this flow (singlethread) */
		}
	} catch(std::exception& e) {
		std::cout << kManagerEtq << "Error: " << e.what() << std::endl;
		std::cout << kManagerEtq << "Now exiting!" << std::endl;
	}
}
/* Enqueuer thread execution flow | Sharing a SynQueue */
void Manager::exec_enqueuer() {
	using boost::asio::ip::tcp;
	std::cout << kManagerEtq << kManager1Etq << "Running!" << std::endl;
	try {
		/* Listen on set port for incoming connections and retrieve data to
		 * put in the queue */
		int port = boost::lexical_cast<int>(this->_config.man_port_toui_bind);
		boost::asio::io_service io_s;
		tcp::acceptor wf_acceptor(io_s, tcp::endpoint(tcp::v4(), port));
		// Looping for accepting incoming requests
		for (;;) {
			tcp::iostream wf_stream;
			Workflow wf;
			std::cout << kManagerEtq << kManager1Etq <<
					"Waiting for flow to be sent..." << std::endl;
			wf_acceptor.accept(*(wf_stream.rdbuf())); /* Accepting */
			boost::archive::binary_iarchive ia(wf_stream);
			ia >> wf; /* Here I have the workflow */
			wf_stream.close();
			std::cout << kManagerEtq << kManager1Etq <<
					"Workflow arrived: WID=" << wf.get_id() <<
					" (" << wf.size() << " tasks). Now enqueueing..."  <<
					std::endl;
			// Now enqueueing the workflow
			this->_wf_synqueue.enqueue(wf);
			std::cout << kManagerEtq << kManager1Etq <<
					"Workflow " << wf.get_id() << " enqueued!" << std::endl;
		}
	} catch(std::exception& e) {
		std::cout << kManagerEtq << kManager1Etq <<
				"Error: " << e.what() << std::endl;
		std::cout << kManagerEtq << kManager1Etq <<
				"Program now dying..." << std::endl;
		exit(1);
	}
}
/* Workflow manager (can be a thread) */
void Manager::ManageWorkflow(Workflow workflow) {
	std::cout << kManagerEtq << "WFManager: " << "Managing workflow "
			<< workflow.get_id() << std::endl;
	// TODO
}
/* Bonjour */
void Manager::bonjour() {
	std::cout << kManagerEtq << "Initialization sequence now started..." <<
				std::endl;
	std::cout << kManagerEtq << "Configuration loaded: bind_touiIP: this" <<
			", bind_touiPORT: " <<
			this->_config.man_port_toui_bind << "; todiscIP: " <<
			this->_config.man_ip_todisc << ", todiscPORT: " <<
			this->_config.man_port_todisc << std::endl;
	std::cout << kManagerEtq << "Initialization completed!" << std::endl;
}
